<template>
  <div ref="list" class="infinite-list-container" @scroll="scrollEvent">
    <div
      class="infinite-list-phantom"
      :style="{ height: person.listHeight + 'px' }"
    ></div>
    <div class="infinite-list" :style="{ transform: person.getTransform }">
      <div
        ref="items"
        class="infinite-list-item"
        v-for="item in person.visibleData"
        :key="item.id"
        :style="{ height: itemSize + 'px', lineHeight: itemSize + 'px' }"
      >
        {{ item.value }}
      </div>
    </div>
  </div>
</template>

<script lang="ts" >
import { defineComponent, onMounted, reactive, ref, computed } from "vue";
interface Iperson {
  screenHeight: number;
  startOffset: number;
  start: number;
  end: number;
  [propName: string]: any; //propName为形参
}
export default defineComponent({
  props: {
    //所有列表数据
    listData: {
      type: Array,
      default: () => [],
    },
    //每项高度
    itemSize: {
      type: Number,
      default: 200,
    },
  },
  setup(props) {
    let list = ref<any>(null);
    let person: Iperson = reactive({
      //可视区域高度
      screenHeight: 0,
      //偏移量
      startOffset: 0,
      //起始索引
      start: 0,
      //结束索引
      end: 0,
    });
    person.listHeight = computed(() => {
      return props.listData.length * props.itemSize;
    });
    //可显示的列表项数
    person.visibleCount = computed(() => {
      return Math.ceil(person.screenHeight / props.itemSize);
    });
    //偏移量对应的style
    person.getTransform = computed(() => {
      return `translate3d(0,${person.startOffset}px,0)`;
    });
    //获取真实显示列表数据
    person.visibleData = computed(() => {
      return props.listData.slice(
        person.start,
        Math.min(person.end, props.listData.length)
      );
    });

    onMounted(() => {
      person.screenHeight = list.value.clientHeight;
      person.start = 0;
      person.end = person.start + person.visibleCount;
    });
    function scrollEvent() {
      //当前滚动位置
      let scrollTop = list.value.scrollTop;
      //此时的开始索引
      person.start = Math.floor(scrollTop / props.itemSize);
      //此时的结束索引
      person.end = person.start + person.visibleCount;
      //此时的偏移量
      person.startOffset = scrollTop - (scrollTop % props.itemSize);
    }
    return { scrollEvent, person, list };
  },
});
</script>

<style lang="scss" scoped>
.infinite-list-container {
  height: 100%;
  overflow: auto;
  position: relative;
  -webkit-overflow-scrolling: touch;
}

.infinite-list-phantom {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  z-index: -1;
}

.infinite-list {
  left: 0;
  right: 0;
  top: 0;
  position: absolute;
  text-align: center;
}

.infinite-list-item {
  padding: 10px;
  color: #555;
  box-sizing: border-box;
  border-bottom: 1px solid #999;
}
</style>